﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Tab2Sql
{
    /// <summary>
    /// The top box used for user input / datasets. Extends to RichTextBox to enable slightly better file loading
    /// when dealing with large files.
    /// Additionally, all load processes are profiled via StopWatch object which is reported in the status box on the main form.
    /// Most of this class is not in use at time of writing. (2011-02-20).
    /// </summary>
    public class inRtbox : RichTextBox
    {
        /// <summary>
        /// Upper limit for text in unabridged mode. Once the text exceeds this length then the text displayed in the text control is a truncated version of the full text.
        /// </summary>

        public const int MAXIMUM_TEXT_LENGTH = 100000;

        public override string Text {
            get {
                
                if (!IsTextAbridged)
                {
                    return base.Text;
                } else
                {
                    return unabridgedText.ToString ();
                }
                
            }
            set {
                if (!IsTextAbridged)
                {
                    base.Text = value;
                } else
                {
                    unabridgedText = new StringBuilder (value);
                    
                }
                
            }
        }
        public int VisibleLineCount {
            get { return base.Lines.Length; }
        }

        public int FullLineCount {
            get {
                if (!IsTextAbridged)
                {
                    return base.Lines.Length;
                } else
                {
                    return unabridgedText.ToString ().Split ('\n').Length;
                }
            }
        }





        public override int TextLength {
            get {
                if (!IsTextAbridged)
                {
                    return base.TextLength;
                } else
                {
                    return unabridgedText.Length;
                }
            }
        }

        private bool isTextAbridged;


        public bool IsTextAbridged {
            get {
                if (unabridgedText == null)
                {
                    return false;
                } else
                {
                    return isTextAbridged;
                }
            }
        }

        private StringBuilder unabridgedText;




        /// <summary>
        /// Default / only constructor.
        /// </summary>
        public inRtbox ()
        {
            isTextAbridged = false;

            
            TableName = "";
            
        }


        /// <summary>
        /// the table name - e.g. #foo
        /// </summary>
        public string TableName { get; set; }

        System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch ();
        /// <summary>
        /// Duration of operations in ms.
        /// </summary>
        public long Milliseconds {
            get { return s.ElapsedMilliseconds; }
        }
        /// <summary>
        /// Duration as string.
        /// </summary>
        public string Mseconds {
            get { return Milliseconds.ToString () + "ms."; }
        }
        /// <summary>
        /// Has file been loaded?
        /// </summary>
        public bool IsDone = false;

        private string importedFileName = "";
        /// <summary>
        /// Source file name.
        /// </summary>
        public string ImportedFileName {
            get { return importedFileName; }
            set { importedFileName = value; }
        }
        /// <summary>
        /// This doesn't do anything.
        /// </summary>
        /// <returns>
        /// A <see cref="System.Int32"/>
        /// </returns>
        public int DoLoadFile ()
        {
            
            return 0;
        }

        /// <summary>
        /// Maximum permissible text value for string box.
        /// </summary>




        /// <summary>
        /// Overloaded version of append text which checks text length before adding.
        /// </summary>
        /// <param name="sb">
        /// A <see cref="StringBuilder"/>
        /// </param>
        public void AppendText (ref StringBuilder sb)
        {
            // First things first. Are we already in abridgement mode? If we are, then
            if (isTextAbridged)
            {
                unabridgedText.Append (sb);
                return;

            }
            // If we're not in abridgement mode, let's check if this will send us over the limit
            if (sb.Length <= MAXIMUM_TEXT_LENGTH && sb.Length + base.TextLength <= MAXIMUM_TEXT_LENGTH)
            {
                base.AppendText (sb.ToString ());
                return;

            }
            else
            {
                // So, we activate abridgement mode.
                // We create a new stringbuilder, add in whatever the current string is and add the new text.
                int currentTextLength = base.TextLength;
                //for convenience.

                isTextAbridged = true;


                unabridgedText = new StringBuilder (MAXIMUM_TEXT_LENGTH + sb.Length);
                unabridgedText.Append (base.Text);
                unabridgedText.Append (sb);
                base.AppendText (sb.ToString ().Substring (0, MAXIMUM_TEXT_LENGTH - currentTextLength));
                return;




            }
            return;
            
        }

        public override void ResetText ()
        {
            if (isTextAbridged)
            {
                unabridgedText = null;
                isTextAbridged = false;

            }
            base.ResetText ();
        }


        private void doCopy (FileStream fs1, FileStream fs2)
        {
            int i;
            do
            {
                i = fs1.ReadByte ();
                if (i != -1)
                    fs2.WriteByte ((byte)i);
            } while (i != -1);
        }
        private int doCopy (string path, string tempPath)
        {
            int result = -1;
            try
            {
                File.Copy (path, tempPath, true);
                result = 0;
            } catch
            {
                result = 1;
            }
            return result;
        }


        /// <summary>
        /// Loads text from supplied path in the rich text box control. Attempts to check supplied file is not over
        /// globally established file size limit. By default if excessively large files were supplied the RichTextBox control
        /// either crashed or raised an exception somewhere along the chain but outwardly did nothing.
        /// If file is over established limit then a chunk of the file is loaded representing the proportion which fit
        /// under the file limit.
        /// The problem with this approach is that this tends to mangle the end line and produce an abnormal number of columns.
        /// </summary>
        /// <param name="tpath">
        /// A <see cref="System.String"/>
        /// </param>
        /// <returns>
        /// A <see cref="System.Int32"/>
        /// </returns>
        public int DoLoadFile (string tpath)
        {
            
            s.Start ();
            using (FileStream fileStream = new FileStream (tpath, FileMode.Open, FileAccess.Read))
            {
                long fl = fileStream.Length;
                if (fl > Properties.Settings.Default.MaximumFileSizeGlobal)
                {
                    string tmpName = Path.GetTempFileName ();
                    FileStream fileStream2 = new FileStream (tmpName, FileMode.Open, FileAccess.ReadWrite);
                    byte[] m = new byte[Convert.ToInt32 (Properties.Settings.Default.MaximumFileSizeGlobal)];
                    int m2 = fileStream.Read (m, 0, Convert.ToInt32 (Properties.Settings.Default.MaximumFileSizeGlobal));
                    fileStream2.Write (m, 0, Convert.ToInt32 (Properties.Settings.Default.MaximumFileSizeGlobal));
                    this.LoadFile (tmpName);
                    tpath = tpath + "--->" + tmpName;
                    
                } else
                {
                    this.LoadFile (fileStream, RichTextBoxStreamType.PlainText);
                }
            }
            IsDone = true;
            ImportedFileName = tpath;
            s.Stop ();
            return 0;
        }


        /// <summary>
        /// Loads text from supplied path in the rich text box control. Attempts to check supplied file is not over
        /// globally established file size limit. By default if excessively large files were supplied the RichTextBox control
        /// either crashed or raised an exception somewhere along the chain but outwardly did nothing.
        /// If file is over established limit then a chunk of the file is loaded representing the proportion which fit
        /// under the file limit.
        /// The problem with this approach is that this tends to mangle the end line and produce an abnormal number of columns.
        /// OVERLOAD:This utilises a (hard coded) upper limit of 20 meg (or ~19.073mb)
        /// </summary>
        /// <param name="path">
        /// A <see cref="System.String"/>
        /// </param>
        /// <param name="isRedundant">
        /// A <see cref="System.Boolean"/>. This does not actually get read so supply true or false to active this particular overload.
        /// </param>
        /// <returns>
        /// A <see cref="System.Int32"/>
        /// </returns>
        public int DoLoadFile (string path, bool isRedundant)
        {
            
            FileInfo f = new FileInfo (path);
            // HACK:Hardcoded value.
            
            if (f.Length > 20000000)
            {
                SplitFile (path);
            }
            
            s.Start ();
            string tPath = DGen.DFile.uniqueFileName ();
            
            if (doCopy (path, tPath) == 0)
            {
                using (FileStream fileStream = new FileStream (tPath, FileMode.Open, FileAccess.ReadWrite))
                {
                    fileStream.SetLength (Math.Min (10000000, fileStream.Length));
                    this.LoadFile (fileStream, RichTextBoxStreamType.PlainText);
                }
                IsDone = true;
                ImportedFileName = tPath;
            }
            s.Stop ();
            return 0;
            
        }




        /// <summary>
        /// Splits supplied file name into chunks of 65536 * 256 bytes (~16.077mb). The initial file is not affected
        /// the contents are put in the temporary directory with unique names.
        /// The return value is meaningless.
        /// </summary>
        /// <param name="fName">
        /// A <see cref="System.String"/>
        /// </param>
        /// <returns>
        /// A <see cref="System.Int32"/>
        /// </returns>
        public int SplitFile (string fName)
        {
            string firstLine = DGen.DFile.GetLineAt (fName, 0);
            string guidFileName = DGen.DFile.uniqueFileName ();
            File.WriteAllText (guidFileName, firstLine);
            
            
            int index = 0;
            long maxFileSize = 65536 * 256;
            byte[] buffer = new byte[65536];
            
            string tPath = Path.GetDirectoryName (fName);
            using (Stream source = File.OpenRead (fName))
            {
                while (source.Position < source.Length)
                {
                    index++;
                    
                    // Create a new sub File, and read into t
                    string newFileName = Path.Combine (tPath, Path.GetFileNameWithoutExtension (fName));
                    newFileName += index.ToString () + Path.GetExtension (fName);
                    
                    using (Stream destination = File.OpenWrite (newFileName))
                    {
                        while (destination.Position < maxFileSize)
                        {
                            // Work out how many bytes to read
                            int bytes = source.Read (buffer, 0, (int)Math.Min (maxFileSize, buffer.Length));
                            destination.Write (buffer, 0, bytes);
                            
                            // Are we at the end of the file?
                            if (bytes < Math.Min (maxFileSize, buffer.Length))
                            {
                                break;
                            }
                        }
                    }
                    DGen.DFile.PutLineAt (newFileName, 0, firstLine);
                    
                }
            }
            
            return 0;
        }
    }
}

